package btree;

import global.PageId;
import global.RID;
import global.SystemDefs;
import heap.InvalidSlotNumberException;

import java.io.IOException;
import java.util.ArrayList;

import bufmgr.BufMgrException;
import bufmgr.BufferPoolExceededException;
import bufmgr.HashEntryNotFoundException;
import bufmgr.HashOperationException;
import bufmgr.InvalidFrameNumberException;
import bufmgr.PageNotReadException;
import bufmgr.PagePinnedException;
import bufmgr.PageUnpinnedException;
import bufmgr.ReplacerException;
import diskmgr.DiskMgrException;
import diskmgr.FileIOException;
import diskmgr.InvalidPageNumberException;

public class BTFileScan extends IndexFileScan {

	private ArrayList<KeyDataEntry> leafData;
	private int iterator;
	private int endIterator;
	private static BTLeafPage leafPage;
	private static PageId pid;

	public BTFileScan(KeyClass lo_key, KeyClass hi_key) throws FileIOException,
			InvalidPageNumberException, DiskMgrException, IOException,
			ReplacerException, HashOperationException, PageUnpinnedException,
			InvalidFrameNumberException, PageNotReadException,
			BufferPoolExceededException, PagePinnedException, BufMgrException,
			KeyNotMatchException, NodeNotMatchException, ConvertException,
			HashEntryNotFoundException, ConstructPageException {
		leafData = new ArrayList<KeyDataEntry>();
		intialize();
		setIterator(lo_key, hi_key);
	}

	/**
	 * this method sets the header page of the BTree file we want to scan
	 * 
	 * @param page
	 * @throws KeyNotMatchException
	 * @throws NodeNotMatchException
	 * @throws ConvertException
	 * @throws IOException
	 * @throws ConstructPageException
	 */
	public static void setHead(BTIndexPage page) throws KeyNotMatchException,
			NodeNotMatchException, ConvertException, IOException,
			ConstructPageException {
		PageId pidd = page.getLeftLink();
		pid = pidd;
		leafPage = new BTLeafPage(pidd, page.keyType);
	}

	/**
	 * In this method we loop the all BTree file and fill the leaf data with the
	 * keyDataEntries so that we can scan the all records in the file
	 * 
	 * @throws KeyNotMatchException
	 * @throws NodeNotMatchException
	 * @throws ConvertException
	 * @throws IOException
	 * @throws ReplacerException
	 * @throws PageUnpinnedException
	 * @throws HashEntryNotFoundException
	 * @throws InvalidFrameNumberException
	 * @throws ConstructPageException
	 */
	public void intialize() throws KeyNotMatchException, NodeNotMatchException,
			ConvertException, IOException, ReplacerException,
			PageUnpinnedException, HashEntryNotFoundException,
			InvalidFrameNumberException, ConstructPageException {

		BTLeafPage page = new BTLeafPage(pid, leafPage.keyType);

		RID rid = new RID();
		leafData.add(page.getFirst(rid));
		// System.out.println(leafData.get(0).key);
		while (true) {
			KeyDataEntry temp = page.getNext(rid);
			// end of the page or end of the records in this page
			if (temp == null) {
				PageId pid = page.getNextPage();
				SystemDefs.JavabaseBM.unpinPage(page.getCurPage(), false);
				if ((pid + "").equals("-1"))
					break;
				page = new BTLeafPage(pid, page.keyType);
				if (page.getSlotCnt() == 0) {
					// SystemDefs.JavabaseBM.unpinPage(page.getCurPage(),
					// false);
					break;
				}
				rid = new RID();
				leafData.add(page.getFirst(rid));
			} else {
				// System.out.println(temp.key);
				leafData.add(temp);
			}
		}
	}

	/**
	 * This method sets the the range to be scanned
	 * 
	 * @param lo_key
	 * @param hi_key
	 * @throws KeyNotMatchException
	 */
	public void setIterator(KeyClass lo_key, KeyClass hi_key)
			throws KeyNotMatchException {

		// if both are null then scan the whole file
		if (lo_key == null && hi_key == null) {
			iterator = 0;
			endIterator = leafData.size() - 1;
			// if low key is null and the high key has a value then scan the
			// specified range.
		} else if (lo_key == null && hi_key != null) {
			iterator = 0;
			for (int i = leafData.size() - 1; i >= 0; i--) {
				if (BT.keyCompare(leafData.get(i).key, hi_key) <= 0) {
					endIterator = i;
					break;
				}
			}
			// if high key is null and the low key has a value then scan the
			// specified range.
		} else if (lo_key != null && hi_key == null) {
			endIterator = leafData.size() - 1;
			for (int i = 0; i < leafData.size(); i++) {
				if (BT.keyCompare(leafData.get(i).key, lo_key) >= 0) {
					iterator = i;
					break;
				}
			}
			// if both low, high key have the same value then scan the specified
			// key.
		} else if (lo_key != null && hi_key != null
				&& BT.keyCompare(lo_key, hi_key) == 0) {
			for (int i = leafData.size() - 1; i >= 0; i--) {
				if (BT.keyCompare(leafData.get(i).key, hi_key) == 0) {
					endIterator = i;
					break;
				}
			}
			for (int i = 0; i < leafData.size(); i++) {
				if (BT.keyCompare(leafData.get(i).key, lo_key) == 0) {
					iterator = i;
					break;
				}
			}
			// if both low, high key have different values then scan the
			// specified range.
		} else if (lo_key != null && hi_key != null
				&& BT.keyCompare(lo_key, hi_key) < 0) {
			for (int i = leafData.size() - 1; i >= 0; i--) {
				if (BT.keyCompare(leafData.get(i).key, hi_key) <= 0) {
					endIterator = i;
					break;
				}
			}
			for (int i = 0; i < leafData.size(); i++) {
				if (BT.keyCompare(leafData.get(i).key, lo_key) >= 0) {
					iterator = i;
					break;
				}
			}
		}
	}

	@Override
	/*
	 * return the next KeyDataEntry
	 */
	public KeyDataEntry get_next() {
		if (iterator <= endIterator && leafData.size() > 0)
			return leafData.get(iterator++);
		return null;
	}

	// rid = ((LeafData) leafData.get(iterator - 1).data).getData();

	@Override
	public void delete_current() throws ConstructPageException, IOException,
			KeyNotMatchException, NodeNotMatchException, ConvertException,
			ReplacerException, PageUnpinnedException,
			HashEntryNotFoundException, InvalidFrameNumberException,
			InvalidSlotNumberException, DeleteRecException {
		BTLeafPage page = new BTLeafPage(pid, leafPage.keyType);
		KeyDataEntry deleted = null;
		if (iterator > 0)
			deleted = leafData.get(iterator - 1);
		if (deleted == null)
			return;
		RID rid = new RID();
		KeyDataEntry current = page.getFirst(rid);
		while (!(current.key + "").equals(deleted.key + "")) {
			current = page.getNext(rid);
			System.out.println(current + "    hi, there");
			// end of the page or end of the records in this page
			if (current == null) {
				PageId pid = page.getNextPage();
				SystemDefs.JavabaseBM.unpinPage(page.getCurPage(), false);
				page = new BTLeafPage(pid, page.keyType);
				if (page.getSlotCnt() == 0) {
					return;
				}
				rid = new RID();
				current = page.getFirst(rid);
			}
		}
		// delete here
		page.delEntry(current);
		leafData.remove(--iterator);
		SystemDefs.JavabaseBM.unpinPage(page.getCurPage(), false);
	}

	// TODO it
	@Override
	public int keysize() {
		return BTreeFile.getMaxSize();
	}

	public void DestroyBTreeFileScan() throws ReplacerException,
			PageUnpinnedException, HashEntryNotFoundException,
			InvalidFrameNumberException {
		leafData = null;
		SystemDefs.JavabaseBM.unpinPage(pid, false);
	}

	public ArrayList<KeyDataEntry> getLeaf() {
		return leafData;
	}
}
